
/**
 * Author: Wang Haitao
 * Date: 2024-xx-xx
 * Description: 这个文件关于INA228的大部分驱动实现
 * Code Copy：拷贝代码，请加上作者信息。
 */
#include "ina22x_driver.h"
#include <stdio.h>

#define    INA228_VBus_LSB		        0.1953125f                     //总线电压mV
#define    INA228_Shunt0_LSB            0.3125f	                       //分流电压uV
#define    INA228_Shunt1_LSB            0.078125f	                   //分流电压uV
#define    INA228_Temp_LSB              7.8125f                        //温度m℃
#define    INA228_Current_LSB 	        (INA22x_Max_Current_mA/524288) //电流mA
#define    INA228_POWER_LSB             (3.2f*INA228_Current_LSB)      //功率mW
#define    INA228_ENERGY_LSB            (16*3.2f*INA228_Current_LSB)   //焦耳
#define    INA228_CHARGE_LSB            INA228_Current_LSB             //库仑

#define    INA228_VBus_BOVL_LSB         3.125f                         //总线过压mV
#define    INA228_VBus_BUVL_LSB         3.125f                         //总线欠压mV
#define    INA228_Shunt0_SUVL_SOVL_LSB  0.005f                         //分流欠压mV
#define    INA228_Shunt1_SUVL_SOVL_LSB  0.00125f                       //分流欠压mV
#define    INA228_Temp_TOL_LSB          7.8125f                        //超温m℃

typedef enum
{
	INA228_CONFIG          = 0x0, //配置
	INA228_ADC_CONFIG      = 0x1, //ADC配置
	INA228_SHUNT_CAL       = 0x2, //分流校准
	INA228_SHUNT_TEMPCO    = 0x3, //分流温度系数
	INA228_VSHUNT          = 0x4, //分流电压测量
	INA228_VBUS            = 0x5, //总线电压测量
	INA228_DIETEMP         = 0x6, //温度测量
	INA228_CURRENT         = 0x7, //电流结果
	INA228_POWER           = 0x8, //功率结果
	INA228_ENERGY          = 0x9, //电能结果
	INA228_CHARGE          = 0xA, //电荷结果
	INA228_DIAG_ALRT       = 0xB, //诊断标志和警报
	INA228_SOVL            = 0xC, //分流过压阈值
	INA228_SUVL            = 0xD, //分流欠压阈值
	INA228_BOVL            = 0xE, //总线过压阈值
	INA228_BUVL            = 0xF, //总线欠压阈值
	INA228_TEMP_LIMIT      = 0x10,//温度高于上限阈值
	INA228_PWR_LIMIT       = 0x11,//功率高于上限阈值
	INA228_MANUFACTURER_ID = 0x3E,//制造商ID
	INA228_DEVICE_ID       = 0x3F,//器件ID
}WHT_INA228_Cmd_enum;

/**********************驱动初始化配置*********************/
static SemaphoreHandle_t Tx_xSemaphore;//二值信号量句柄
static SemaphoreHandle_t Rx_xSemaphore;
static WHT_I2C_Cache_t WHT_Cache;
static void WHT_Tx_Idle_IT_Callback(void);
static void WHT_Rx_Idle_IT_Callback(void);
static void WHT_I2C_BUS_Init(void)
{
    if (WHT_I2C_BUS1_Init() != 0)
    {
        /* 总线初始化失败 */
        printf("INA228:I2C BUS Init ERROR\r\n");
        return;
    }
    WHT_Cache.Cmd_Count = 1;
    WHT_Cache.Read_Finish_IT_CB = WHT_Rx_Idle_IT_Callback;
    WHT_Cache.Write_Finish_IT_CB = WHT_Tx_Idle_IT_Callback;

    Tx_xSemaphore = xSemaphoreCreateBinary();
    Rx_xSemaphore = xSemaphoreCreateBinary();
}
/**********************驱动初始化配置*********************/

/************************** read & write *******************************/
/*发送空闲中断回调函数*/
static void WHT_Tx_Idle_IT_Callback(void)
{
    BaseType_t xHigherPriorityTaskWoken;

    xSemaphoreGiveFromISR(Tx_xSemaphore, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
/*接收空闲中断回调函数*/
static void WHT_Rx_Idle_IT_Callback(void)
{
    BaseType_t xHigherPriorityTaskWoken;

    xSemaphoreGiveFromISR(Rx_xSemaphore, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
static void WHT_I2C_Write(uint8_t addr_7bit, WHT_INA228_Cmd_enum cmd, uint8_t* const input_buf, uint32_t count)
{
    while (WHT_I2C_BUS1->Mutex == I2C_Lock)
        vTaskDelay(1);
    WHT_Cache.Addr_7Bit = addr_7bit;
    WHT_Cache.Dir = I2C_Write;
    WHT_Cache.Cmd[0] = cmd;
    WHT_Cache.Buffer = input_buf;
    WHT_Cache.Buffer_Count = count;

    TickType_t Tick_Count = count * 9 * 1000  / WHT_I2C_BUS1->Config.I2C_Rate_Hz / portTICK_RATE_MS;//单位ms/Tick
    if (Tick_Count)
        WHT_I2C_BUS_OPS.Read_Write_DMA(WHT_I2C_BUS1, &WHT_Cache);
    else
        WHT_I2C_BUS_OPS.Read_Write_IT(WHT_I2C_BUS1, &WHT_Cache);
    
    if (WHT_I2C_BUS1->Mutex == I2C_Unlock)//传输结束
    {
        /*用户可以加入判断是否有错误相关代码*/
    }
    else
        xSemaphoreTake(Tx_xSemaphore, portMAX_DELAY);//等待信号量
}
static void WHT_I2C_Read(uint8_t addr_7bit, WHT_INA228_Cmd_enum cmd, uint8_t* const output_buf, uint32_t count)
{
    while (WHT_I2C_BUS1->Mutex == I2C_Lock)//等待互斥锁成功打开
        vTaskDelay(1);
    WHT_Cache.Addr_7Bit = addr_7bit;
    WHT_Cache.Dir = I2C_Read;
    WHT_Cache.Cmd[0] = cmd;
    WHT_Cache.Buffer = output_buf;
    WHT_Cache.Buffer_Count = count;

    TickType_t Tick_Count = count * 9 * 1000  / WHT_I2C_BUS1->Config.I2C_Rate_Hz / portTICK_RATE_MS;//单位ms/Tick
    if (Tick_Count)
        WHT_I2C_BUS_OPS.Read_Write_DMA(WHT_I2C_BUS1, &WHT_Cache);
    else
        WHT_I2C_BUS_OPS.Read_Write_IT(WHT_I2C_BUS1, &WHT_Cache);
    
    if (WHT_I2C_BUS1->Mutex == I2C_Unlock)//传输结束
    {
        /*用户可以加入判断是否有错误相关代码*/
    }
    else
        xSemaphoreTake(Rx_xSemaphore, portMAX_DELAY);//等待信号量
}
static uint8_t WHT_INA228_Scan_I2C_Slave_Count(void)
{
    return WHT_I2C_BUS_OPS.Scan_Mount_Count(WHT_I2C_BUS1);
}
/************************** read & write *******************************/


static uint8_t TRx_Buffer[8];

/* 分流电压 */
static void WHT_INA28_Get_Shunt_Voltage(WHT_INA228_Info_t* info)
{
    int32_t Temp;

    WHT_I2C_Read(info->Config.Addr, INA228_VSHUNT, TRx_Buffer, 3);
    Temp = (TRx_Buffer[0]<<16 | TRx_Buffer[1]<<8 | TRx_Buffer[2])>>4;
    if (Temp & 0x80000)
        Temp |=0xfff00000;
    info->Result->Shunt_Voltage_uV = Temp * INA228_Shunt0_LSB;
}
/* 总线电流 */
static void WHT_INA228_Get_IBus_Current(WHT_INA228_Info_t* info)
{
    int32_t Temp;

    WHT_I2C_Read(info->Config.Addr, INA228_CURRENT, TRx_Buffer, 3);
    Temp = (TRx_Buffer[0]<<16 | TRx_Buffer[1]<<8 | TRx_Buffer[2])>>4;
    if (Temp & 0x80000)
        Temp |=0xfff00000;
    info->Result->Current_mA = Temp * INA228_Current_LSB;
}
/* 总线电压 */
static void WHT_INA228_Get_VBus_Voltage(WHT_INA228_Info_t* info)
{
    uint32_t Temp;

    WHT_I2C_Read(info->Config.Addr, INA228_VBUS, TRx_Buffer, 3);
    Temp = (TRx_Buffer[0]<<16 | TRx_Buffer[1]<<8 | TRx_Buffer[2])>>4;
    info->Result->Voltage_mV =Temp * INA228_VBus_LSB * info->Config.Calibration_VBus_Gain_Val;
}
/* 总线功率 */
static void WHT_INA228_Get_WBus_Power(WHT_INA228_Info_t* info)
{
    uint32_t Temp;

    WHT_I2C_Read(info->Config.Addr, INA228_POWER, TRx_Buffer, 3);
    Temp = TRx_Buffer[0]<<16 | TRx_Buffer[1]<<8 | TRx_Buffer[2];
    info->Result->Power_mW =Temp * INA228_POWER_LSB;
}
/* 芯片温度 */
static void WHT_INA228_Get_Temp(WHT_INA228_Info_t* info)
{
    int16_t Temp;

    WHT_I2C_Read(info->Config.Addr, INA228_DIETEMP, TRx_Buffer, 2);
    Temp = TRx_Buffer[0]<<8 | TRx_Buffer[1];
    info->Result->Temp_mC = Temp * INA228_Temp_LSB;
}
/* 获取默认校准值 */
static uint16_t WHT_INA228_Get_Default_CAL(WHT_INA228_Info_t* info)
{
    return (uint16_t)(13107.2f * INA228_Current_LSB * info->Config.Sampling_Res_Milliohm);
}
/* 设置电流校准值 */
static void WHT_INA228_Set_CAL(WHT_INA228_Info_t* info)
{
    TRx_Buffer[0] = info->Config.Calibration_Shunt_Gain_Val >> 8;
    TRx_Buffer[1] = info->Config.Calibration_Shunt_Gain_Val & 0xff;
    WHT_I2C_Write(info->Config.Addr, INA228_SHUNT_CAL, TRx_Buffer, 2);//设置电流分辨率
    //WHT_I2C_Write(info->Config.Addr, INA228_SHUNT_TEMPCO, TRx_Buffer, 2);//设置温度分辨率
}
/* 设置采样转换时间 */
static void WHT_INA228_Set_CNV_Time(WHT_INA228_Info_t* info)
{
    uint16_t Temp;

    WHT_I2C_Read(info->Config.Addr, INA228_ADC_CONFIG, TRx_Buffer, 2);
    Temp = (TRx_Buffer[0]<<8) | TRx_Buffer[1];
    Temp &= 0xf000;
    switch (info->Config.Convert_Time)
    {
    case CNV_Time_5ms: Temp |= (3<<9) | (3<<6) | (3<<3) | 2;break;
    case CNV_Time_9ms: Temp |= (4<<9) | (4<<6) | (4<<3) | 2;break;
    case CNV_Time_17ms:Temp |= (5<<9) | (5<<6) | (5<<3) | 2;break;
    case CNV_Time_19ms:Temp |= (3<<9) | (3<<6) | (3<<3) | 3;break;
    default:
        return;
    }
    TRx_Buffer[0] = Temp>>8;
    TRx_Buffer[1] = Temp&0xff;
    WHT_I2C_Write(info->Config.Addr, INA228_ADC_CONFIG, TRx_Buffer, 2);
}
/* 设置芯片工作模式 */
static void WHT_INA228_Set_Work_Mode(WHT_INA228_Info_t* info)
{
    WHT_I2C_Read(info->Config.Addr, INA228_ADC_CONFIG, TRx_Buffer, 2);
    TRx_Buffer[0] &= 0x0f;
    TRx_Buffer[0] |= info->Config.Mode << 12;
    WHT_I2C_Write(info->Config.Addr, INA228_ADC_CONFIG, TRx_Buffer, 2);
}
/* 设置报警功能 */
static void WHT_INA228_Set_Alert_Func(WHT_INA228_Info_t* info)
{
    uint16_t Temp;

    WHT_I2C_Read(info->Config.Addr, INA228_DIAG_ALRT, TRx_Buffer, 2);
    Temp = TRx_Buffer[0] << 8 | TRx_Buffer[1];
    Temp &= 0x01;
    Temp |= info->Config.Alert_ALATCH << 15;
    Temp |= info->Config.Alert_CNVR << 14;
    Temp |= info->Config.Alert_SLOWALERT << 13;
    Temp |= info->Config.Alert_APOL << 12;
    Temp |= info->Config.Alert_TMPOL << 7;
    Temp |= info->Config.Alert_SHNTOL << 6;
    Temp |= info->Config.Alert_SHNTUL << 5;
    Temp |= info->Config.Alert_BUSOL << 4;
    Temp |= info->Config.Alert_BUSUL << 3;
    Temp |= info->Config.Alert_POL << 2;
    TRx_Buffer[0] = Temp >> 8;
    TRx_Buffer[1] = Temp & 0xff;
    WHT_I2C_Write(info->Config.Addr, INA228_DIAG_ALRT, TRx_Buffer, 2);
}
/* 获取报警标志 */
static uint16_t WHT_INA228_Get_Alert_Flag(WHT_INA228_Info_t* info)
{
    WHT_I2C_Read(info->Config.Addr, INA228_DIAG_ALRT, TRx_Buffer, 2);
    return (TRx_Buffer[0] << 8) | TRx_Buffer[1];
}
/* 设置报警值 */
static void WHT_INA228_Set_Alert_Value(WHT_INA228_Info_t* info)
{
    TRx_Buffer[0] = info->Config.Alert_Temp_Hig >> 8;
    TRx_Buffer[1] = info->Config.Alert_Temp_Hig & 0xff;
    WHT_I2C_Write(info->Config.Addr, INA228_TEMP_LIMIT, TRx_Buffer, 2);

    TRx_Buffer[0] = info->Config.Alert_Current_Hig >> 8;
    TRx_Buffer[1] = info->Config.Alert_Current_Hig & 0xff;
    WHT_I2C_Write(info->Config.Addr, INA228_SOVL, TRx_Buffer, 2);

    TRx_Buffer[0] = info->Config.Alert_Current_Low >> 8;
    TRx_Buffer[1] = info->Config.Alert_Current_Low & 0xff;
    WHT_I2C_Write(info->Config.Addr, INA228_SUVL, TRx_Buffer, 2);

    TRx_Buffer[0] = info->Config.Alert_Voltage_Hig >> 8;
    TRx_Buffer[1] = info->Config.Alert_Voltage_Hig & 0xff;
    WHT_I2C_Write(info->Config.Addr, INA228_BOVL, TRx_Buffer, 2);

    TRx_Buffer[0] = info->Config.Alert_Voltage_Low >> 8;
    TRx_Buffer[1] = info->Config.Alert_Voltage_Low & 0xff;
    WHT_I2C_Write(info->Config.Addr, INA228_BUVL, TRx_Buffer, 2);

    TRx_Buffer[0] = info->Config.Alert_Power_Hig >> 8;
    TRx_Buffer[1] = info->Config.Alert_Power_Hig & 0xff;
    WHT_I2C_Write(info->Config.Addr, INA228_PWR_LIMIT, TRx_Buffer, 2);
}
/* 采样芯片初始化 */
static void WHT_INA228_Init(WHT_INA228_Info_t* info)
{
    static FlagStatus I2C_BUS_Init_Flag = RESET;
    uint16_t Temp = 0;

    if (I2C_BUS_Init_Flag == RESET)
    {
        I2C_BUS_Init_Flag = SET;
        WHT_I2C_BUS_Init();
        WHT_INA22x_Alert_Status.WHT_Init();
    }
    TRx_Buffer[0] |= SET<<7;
    WHT_I2C_Write(info->Config.Addr, INA228_CONFIG, TRx_Buffer, 2);//重新启动
    vTaskDelay(20);
    Temp |= info->Config.Config_RSTACC << 14;
    Temp |= info->Config.Config_CONVDLY_mS << 6;
    Temp |= info->Config.Config_TEMPCOMP << 5;
    Temp |= info->Config.Config_ADCRANGE << 4;
    TRx_Buffer[0] = Temp >> 8;
    TRx_Buffer[1] = Temp & 0xff;
    WHT_I2C_Write(info->Config.Addr, INA228_CONFIG, TRx_Buffer, 2);//配置

    WHT_INA228_Driver.WHT_Set_Alert_Func(info);
    WHT_INA228_Driver.WHT_Set_Calibration_Shunt_Gain(info);
    WHT_INA228_Driver.WHT_Set_Convert_Time(info);
    WHT_INA228_Driver.WHT_Set_Work_Mode(info);
}

const WHT_INA228_Driver_t WHT_INA228_Driver =
{
	.WHT_Init              = WHT_INA228_Init,
    .WHT_Set_Work_Mode     = WHT_INA228_Set_Work_Mode,
	.WHT_Set_Calibration_Shunt_Gain = WHT_INA228_Set_CAL,
	.WHT_Set_Convert_Time  = WHT_INA228_Set_CNV_Time,
	.WHT_Set_Alert_Func    = WHT_INA228_Set_Alert_Func,
	.WHT_Set_Alert_Value   = WHT_INA228_Set_Alert_Value,
	.WHT_Get_Voltage       = WHT_INA228_Get_VBus_Voltage,
	.WHT_Get_Shunt_Voltage = WHT_INA28_Get_Shunt_Voltage,
	.WHT_Get_Current       = WHT_INA228_Get_IBus_Current,
	.WHT_Get_Power         = WHT_INA228_Get_WBus_Power,
	.WHT_Get_Energy        = NULL,
	.WHT_Get_Charge        = NULL,
	.WHT_Get_Alert_Flag    = WHT_INA228_Get_Alert_Flag,
	.WHT_Get_Default_Calibration_Shunt_Gain = WHT_INA228_Get_Default_CAL,
    .WHT_Scan_I2C_Slave_Count = WHT_INA228_Scan_I2C_Slave_Count,
};
